/*
 * Configuration.java
 * Copyright (C) 2007 Ezequiel Turovetzky
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 3
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see <http://www.gnu.org/licenses/>.
 */
package ar.com.botonera.config;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.jdom.Attribute;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.input.SAXBuilder;
import org.jdom.output.XMLOutputter;

import ar.com.botonera.Application;
import ar.com.botonera.Constants;

/**
 * Contiene la configuraci&oacute;n de la botonera.
 * <br>
 * Debe haber una <i>&uacute;nica</i> instancia de esta clase en
 * toda la aplicaci&oacute;n, y en cada agregado o quitado de un bot&oacute;n,
 * a/de la botonera se debe actualizar este objeto.
 * 
 * <br><br>
 * Este objeto es obtenido a trav&eacute;s del objeto <code>Configurator</code>:
 * <pre>
 * 		Configuration cfg = Configurator.getInstance().getConfiguration();
 * </pre>
 * @author <a href="mailto:tezequiel@gmail.com">Ezequiel Turovetzky</a>
 * @version 1.0
 */
public class Configuration {

    //~ Instance fields --------------------------------------------------------

    /** El documento XML */
    private Document document;
    /** El elemento raiz del XML */
    private Element root;
    /** Cantidad de botones por fila */
    private int buttonsPerRow;
    /** Ancho de la ventana */
    private int windowWidth;
    /** Largo de la ventana */
    private int windowHeight;
    /** El archivo de configuracion */
    private File file;

    //~ Constructors -----------------------------------------------------------

    /**
     * Crea una nueva instancia de esta clase.
     * @param filename El archivo XML desde el cual levantar
     * la configuraci&oacute;n.
     */
    public Configuration(String filename) {
        this(new File(filename));
    }

    /**
     * Crea una nueva instancia de esta clase.
     * @param file El archivo XML desde el cual levantar
     * la configuraci&oacute;n.
     */
    public Configuration(File file) {
        this.file = file;

        try {
            document = new SAXBuilder().build(file);
        } catch(Exception e) {
        	Application.getLogger().fatal("No se pudo levantar la configuracion: " + e.getMessage(), e);
        	System.exit(0);
        }

        root = document.getRootElement();
        buttonsPerRow = Integer.parseInt(root.getAttributeValue(Constants.BUTTONS_PER_ROW_ATTRIBUTE));
        windowWidth = Integer.parseInt(root.getAttributeValue(Constants.WIDTH));
        windowHeight = Integer.parseInt(root.getAttributeValue(Constants.HEIGHT));
    }

    //~ Methods ----------------------------------------------------------------

    /**
     * Devuelve la cantidad de botones por fila que se visualizar&aacute;n
     * en la botonera.
     * @return Cantidad de botones por fila de la botonera.
     */
    public int getButtonsPerRow() {
        return buttonsPerRow;
    }

    /**
     * Establece la cantidad de botones por fila que se visualizar&aacute;n
     * en la botonera.
     * @param buttonsPerRow de botones por fila de la botonera.
     */
    public void setButtonsPerRow(int buttonsPerRow) {
        this.buttonsPerRow = buttonsPerRow;
        Attribute bpr = root.getAttribute(Constants.BUTTONS_PER_ROW_ATTRIBUTE);
        bpr.setValue(String.valueOf(buttonsPerRow));
        update();
    }
    
    /**
     * Devuelve el largo que debe tener la ventana principal
     * de la aplicaci&oacute;n.
     * @return El largo de la ventana principal.
     */
	public int getWindowHeight() {
		return windowHeight;
	}

    /**
     * Devuelve el largo que debe tener la ventana principal
     * de la aplicaci&oacute;n.
     * @param windowHeight El largo de la ventana principal.
     */
	public void setWindowHeight(int windowHeight) {
		this.windowHeight = windowHeight;
        Attribute bpr = root.getAttribute(Constants.HEIGHT);
        bpr.setValue(String.valueOf(windowHeight));
        update();
	}

    /**
     * Devuelve el ancho que debe tener la ventana principal
     * de la aplicaci&oacute;n.
     * @return El ancho de la ventana principal.
     */
	public int getWindowWidth() {
		return windowWidth;
	}

    /**
     * Establece el ancho que debe tener la ventana principal
     * de la aplicaci&oacute;n.
     * @param windowWidth El ancho de la ventana principal.
     */
	public void setWindowWidth(int windowWidth) {
		this.windowWidth = windowWidth;
        Attribute bpr = root.getAttribute(Constants.WIDTH);
        bpr.setValue(String.valueOf(windowWidth));
        update();
	}    

    /**
     * Agrega un elemento al xml de configuraci&oacute;n.
     * @param label El label del bot&oacute;n.
     * @param mp3 El archivo <code>mp3</code> a reproducir por el bot&oacute;.
     */
    @SuppressWarnings("unchecked")
    public void addEntry(String label, String mp3) {
        Entry entry = new ButtonElement(label, mp3);
        root.getChildren().add(entry);
        update();
    }
    
    /**
     * Agrega un elemento al xml de configuraci&oacute;n.
     * @param label El label del bot&oacute;n.
     * @param mp3 El archivo <code>mp3</code> a reproducir por el bot&oacute;.
     */
    public void addEntry(String label, File mp3) {
    	addEntry(label, mp3.getAbsoluteFile().getName());
    }
    
    /**
     * Elimina un tag <code>button</code> del xml.
     * @param label Label del tag a eliminar.
     */
    @SuppressWarnings("unchecked")
    public void removeEntry(String label) {
    	boolean removed = false;
    	for(Iterator<Element> it = root.getChildren().iterator(); it.hasNext() && !removed; ) {
    		Element element = it.next();
    		if(element.getAttributeValue(Constants.LABEL_BUTTON_ATTRIBUTE).equals(label)) {
    			it.remove();
    			removed = true;
    		}
    	}
    	if(removed) {
    		update();
    	}
    }
    
    /**
     * Cambia el label de un bot&oacute;n en el archivo de configuraci&oacute;n
     * @param oldLabel Label del bot&oacute;n,
     * @param newLabel Nuevo label del bot&oacute;n
     */
    @SuppressWarnings("unchecked")
    public void changeLabel(String oldLabel, String newLabel) {
    	boolean encontrado = false;
    	for(Iterator<Element> it = root.getChildren().iterator(); it.hasNext() && !encontrado; ) {
    		Element element = it.next();
    		if(element.getAttributeValue(Constants.LABEL_BUTTON_ATTRIBUTE).equals(oldLabel)) {
    			element.getAttribute(Constants.LABEL_BUTTON_ATTRIBUTE).setValue(newLabel);
    			encontrado = true;
    		}
    	}
    	if(encontrado) {
    		update();
    	}
    }
    
    /**
     * Devuelve una lista con las entradas en el archivo
     * de configuracion.
     * <br>
     * Los futuros cambios a la lista devuelta no se ver&aacute;n reflejados
     * en la lista interna de este objeto.
     * @return Una lista con las entradas de la configuracion.
     */
    @SuppressWarnings("unchecked")
    public List<Entry> entries() {
    	List<Entry> entries = new ArrayList<Entry>();
    	
    	for(Iterator<Element> it = root.getChildren().iterator(); it.hasNext(); ) {
    		Element element = it.next();
    		String label = element.getAttributeValue(Constants.LABEL_BUTTON_ATTRIBUTE);
    		String file  = element.getAttributeValue(Constants.FILE_BUTTON_ATTRIBUTE);
    		
    		Entry entry = new ButtonElement(label, file);
    		entries.add(entry);
    	}
    	
    	return entries;
    }

    /**
     * Actualiza el xml.
     */
    private void update() {
        XMLOutputter out = new XMLOutputter();
        try {
        	Writer writer = new FileWriter(file);
        	out.output(document, writer);
        	writer.close();
        } catch(IOException e) {
        	Application.getLogger().error("No se pudo guardar la configuracion: " + e.getMessage(), e);
        }
    }
}
